use std::time::Duration;

#[cfg(test)]
mod tests {
    #[test]
    fn test_unsafe_seq() {
        super::start();
    }
}

/// 通常可以使用 锁 、信号量( Semaphores)、屏障 (Barrier)和条件变量( Condition Variable)
/// 机制来实现线程同步 。根据不同的并发场景分为很多不同类型的锁，
/// 有互斥锁( Mutex)、读 写锁( RwLock)和 自旋锁( Spinlock)等。
/// 锁的作用是可以保护临界区，同时达到同步和互 斥的效果。不同的锁表现不同，比如互斥锁，
/// 每次只允许单个线程访问临界资源;读写锁可
/// 以同时支持多个线程读或单个线程写 : 自旋锁和互斥锁类似，但当获取锁失败时，
/// 它不会让线程睡眠 ， 而是不断地轮询直到获取锁成功
pub fn start() {
    // 信号量可以在线程间传递信号，也叫作信号灯，它可以为资掘访问进行计数。信号量是 一个非负整数，
    // 所有通过它的线程都会将该整数减1，如果信号量为0， 那么其他线程只能等待。当线程执行完毕离开临界区时，
    // 信号量会再次加1。当信号量只允许设置0和1时， 效果相当于互斥锁。

    // 屏障可以让一系列线程在某个指定的点进行同步。 通过让参与指定屏障区域的线程等待，
    // 直到所有参与钱程都到达指定的点。 而条件变量用来自动阻塞一个线程，直到出现指定的条件，
    // 通常和互斥锁配合使用。
    // 通过一些锁机制，比如互斥锁，也可以用来避免数据竞争。本质上，是通过锁来保护指定区域的原子性的。
    // 有些语言也提供了原子类型来保证原子性，比如 Java、 C++以及Rust。
    // 具有原子性的操作一定是不可分割的，要么全部完成，要么什么都不做。原子类型使用起来简单，
    // 但其背后的机制却一点也不简单，了解其背后的机制有助于更好地使用原子类型。

    // ·钱程管理，在 std::thread模块中定义了管理线程的各种函数和一些底层同步原语。
    // .线程同步，在 std::sync模块中定义了锁、 Channel、条件变量和屏障。

    use std::cell::RefCell;
    use std::thread;

    // 创建线程
    let mut v = vec![];
    for id in 0..5 {
        v.push(thread::spawn(move || println!("in child : {}", id)));
    }
    for child in v {
        child.join();
    }

    // 直接使用 thread::spawn生成的线程，默认没有名称， 并且其栈大小默认为 2MB。
    // 如果想为线程指定名称或者修改默认技大小，则可以使用 thread::Builder 结构体来创建可配置的线程
    let result = thread::Builder::new()
        .name("test".to_string())
        .stack_size(1024)
        .spawn(|| println!("{}", thread::current().name().unwrap()));
    result.unwrap().join();

    // 线程本地存储( Thread Local Storage, TLS) 是每个线程独有的存储空 间， 在这里可 以
    // 存放其他线程无法访问的本地数据
    thread_local! {static FOO: RefCell<i32> = RefCell::new(1);}
    FOO.with(|f| {
        assert_eq!(*f.borrow(), 1);
        *f.borrow_mut() = 2;
        assert_eq!(*f.borrow(), 2);
    });

    thread::spawn(|| {
        FOO.with(|f| {
            assert_eq!(*f.borrow(), 1);
            *f.borrow_mut() = 3;
            assert_eq!(*f.borrow(), 3);
        })
    })
    .join();

    FOO.with(|f| {
        assert_eq!(*f.borrow(), 2);
    });

    // std::thread::park函数提供了阻塞线程的基本能力，而
    // std::thread::Thread::unpark函数可以 将阻塞的线程重启。
    // 可以利用 park 和 unpark 函数来方便地创建一些新的同步原语，比如某 种锁 。
    // 但要注意 park 函数并不能永久地阻塞线程 ，也可以通过 std::thread::park_timeout
    // 来显式指定阻塞超时时间 。

    let handle = thread::spawn(|| {
        println!("Parking thread");
        thread::park();
        println!("Thread unparked");
    });
    thread::sleep(Duration::from_millis(500));
    println!("Unpark the thread");
    handle.thread().unpark();
    handle.join();

    // 除了阻塞/重启的同步原语， std::thread 模块还提供了主动出让当前线程时间片的函数 yield_now。
    // 众所周知，操作系统是抢占式调度线程的，每个钱程都有固定的执行时间片，时间片是由操作系统切分好的，
    // 以便每个线程都可以拥有公平使用 CPU 的机会。但是有时开发者明确知道某个线程在一段时间内会什么都不做，
    // 为了节省计算时间，可以使用 yield_now 函数主动放弃当前操作系统分配的时间片，让给其他线程执行。
}
